#include <stdio.h>
#include <stdlib.h>

#include "ColaPriorizacion.h"
#include "TDA_Lista.h"
#include "TDA_ListaOrdenada.h"
#include "NivelPriorizacion.h"

/* =============================================================================
 * Declaracion Atributos privados
 * =============================================================================
 */
struct ColaPriorizacion {
    tListaOrdenada niveles;
};

typedef struct ColaPriorizacion* _tColaPriorizacion;

/*==============================================================================
 * Declaracion de metodos privados
 *==============================================================================
 */

static tNivelPriorizacion colaPriorizacionGetNivel (tColaPriorizacion pColaPriorizacion, tTrabajo pTrabajo);

static tListaOrdenada colaPriorizacionGetNiveles (tColaPriorizacion pColaPriorizacion);

/*==============================================================================
 * Definicion de metodos publicos
 *==============================================================================
 */

void colaPriorizacionCrear (tColaPriorizacion* pColaPriorizacion){
    
    _tColaPriorizacion cola = NULL;
    
    if (pColaPriorizacion)
    {
        cola = (_tColaPriorizacion) malloc (sizeof(struct ColaPriorizacion));
        
        if (cola)
            listaOrdenadaCrear(&(cola->niveles), &nivelPriorizacionDestruir, &nivelPriorizacionComparar);
    }
    
    *pColaPriorizacion = cola;
}

//------------------------------------------------------------------------------

void colaPriorizacionDestruir (tColaPriorizacion pColaPriorizacion){

    if (pColaPriorizacion)
    {
        listaOrdenadaDestruir(colaPriorizacionGetNiveles(pColaPriorizacion));
        free ((_tColaPriorizacion) pColaPriorizacion);
    }
}

//------------------------------------------------------------------------------

tBool colaPriorizacionPriorizar(tColaPriorizacion pColaPriorizacion, tTrabajo pTrabajo){

    tBool priorizado = FALSE;
    
    if (pColaPriorizacion && pTrabajo)
    {
        tNivelPriorizacion nivelPriorizacion = colaPriorizacionGetNivel(pColaPriorizacion, pTrabajo);
        
        if (NULL == nivelPriorizacion)
        {
            // El trabajo corresponde a un nivel de priorizacion que no se encuentra
            // definido.  Se define el nivel y se lo agrega a la cola de priorizacion.
            
            nivelPriorizacionCrear(&nivelPriorizacion,  trabajoGetPrioridad(pTrabajo));
            listaOrdenadaInsertar(colaPriorizacionGetNiveles(pColaPriorizacion), nivelPriorizacion);
        }
        
        nivelPriorizacionAgregar(nivelPriorizacion, pTrabajo);
        priorizado = TRUE;
    }
    
    return priorizado;
}

//------------------------------------------------------------------------------

void colaPriorizacionRemover(tColaPriorizacion pColaPriorizacion, tTrabajo pTrabajo){

    tNivelPriorizacion nivelPriorizacion = colaPriorizacionGetNivel(pColaPriorizacion, pTrabajo);
    
    if (nivelPriorizacion)
    {
        nivelPriorizacionRemover(nivelPriorizacion, pTrabajo);
        
        if (nivelPriorizacionVacio(nivelPriorizacion))
        {
            listaOrdenadaRemover(colaPriorizacionGetNiveles(pColaPriorizacion), nivelPriorizacion);
            nivelPriorizacionDestruir(nivelPriorizacion);
        }
    }
}

//==============================================================================

struct ColaPriorizacionIterador{
    tLista trabajos;
    tIteradorLista iterador;
};

typedef struct ColaPriorizacionIterador* _tColaPriorizacionIterador;

//------------------------------------------------------------------------------

tColaPriorizacionIterador colaPriorizacionIterador (tColaPriorizacion pColaPriorizacion){
    
    _tColaPriorizacionIterador iterador = NULL;
    
    if (pColaPriorizacion)
    {
        // Instanciar el iterador.
        iterador = (_tColaPriorizacionIterador) malloc (sizeof (struct ColaPriorizacionIterador));
        
        if (iterador)
        {
            // Instancio la lista de trabajos del iterador.
            listaCrear(&(iterador->trabajos), &DestructorDato);
            
            // Popular la lista de trabajos del iterador.
            
            // Recorro la cola de priorizacion en orden secuencial en forma creciente.
            tListaOrdenadaIterador iteradorNiveles = listaOrdenadaIterador(colaPriorizacionGetNiveles(pColaPriorizacion));
            while (listaOrdenadaIteradorMoverSiguiente(iteradorNiveles))
            {
                // Recorro los trabajos del nivel en forma secuencial.
                tNivelPriorizacion nivel = listaOrdenadaIteradorActual(iteradorNiveles);
                
                tIteradorLista iteradorTrabajos = listaObtenerIterador(nivelPriorizacionGetTrabajos(nivel));
                while (listaIteradorMoverSiguiente(iteradorTrabajos))
                {
                    tTrabajo trabajo = listaIteradorActual(iteradorTrabajos);
                    listaAgregarFinal(iterador->trabajos, trabajo);
                }
                listaLiberarIterador(iteradorTrabajos);
            }
            listaOrdenadaIteradorLiberar(iteradorNiveles);
    
            // Iniciar el iterador de la lista de trabajos.
            iterador->iterador = listaObtenerIterador(iterador->trabajos);
        }
    }
    // Retornar el iterador.
    return iterador;
}

//------------------------------------------------------------------------------

void colaPriorizacionIteradorLiberar(tColaPriorizacionIterador pIterador){

    _tColaPriorizacionIterador iterador = (_tColaPriorizacionIterador) pIterador;

    if (pIterador)
    {
        // Finalizar el iterador de trabajos.
        listaLiberarIterador(iterador->iterador);
        
        // Destruir la lista.
        listaDestruir(iterador->trabajos);
        
        // Libero recursos.
        free (iterador);
    }
}

//------------------------------------------------------------------------------

int colaPriorizacionIteradorMoverSiguiente (tColaPriorizacionIterador pIterador){
    
    _tColaPriorizacionIterador iterador = (_tColaPriorizacionIterador) pIterador;
    int val = 0;
    
    if (pIterador)
        val = listaIteradorMoverSiguiente(iterador->iterador);
    
    return val;
}

//------------------------------------------------------------------------------

tTrabajo colaPriorizacionIteradorActual(tColaPriorizacionIterador pIterador){

    _tColaPriorizacionIterador iterador = (_tColaPriorizacionIterador) pIterador;
    tTrabajo trabajo = NULL;

    if (pIterador)
        trabajo = listaIteradorActual(iterador->iterador);
    
    return trabajo;
}

//------------------------------------------------------------------------------

tNivelPriorizacion colaPriorizacionGetNivel (tColaPriorizacion pColaPriorizacion, tTrabajo pTrabajo){
    
    tNivelPriorizacion nivelPriorizacion = NULL;
    
    if (pColaPriorizacion && pTrabajo)
    {
        // Busco el nivel al que pertenece el trabajo.
        tListaOrdenadaIterador iterador = listaOrdenadaIterador(colaPriorizacionGetNiveles(pColaPriorizacion));
        while (listaOrdenadaIteradorMoverSiguiente(iterador) && NULL == nivelPriorizacion)
        {
            tNivelPriorizacion cursor = listaOrdenadaIteradorActual(iterador);
            if (nivelPriorizacionGetNivel(cursor) == trabajoGetPrioridad(pTrabajo))
                nivelPriorizacion = cursor;
        }
        listaOrdenadaIteradorLiberar(iterador);
    }
    return nivelPriorizacion;
}        

//------------------------------------------------------------------------------

tListaOrdenada colaPriorizacionGetNiveles (tColaPriorizacion pColaPriorizacion)
{
    tListaOrdenada cola = NULL;
    
    if (pColaPriorizacion)
        cola = ((_tColaPriorizacion) pColaPriorizacion)->niveles;
                
    return cola;
}
